/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
package com.google.code.bigyang.nio;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;

import com.google.code.bigyang.base.Filter;
import com.google.code.bigyang.base.SessionState;
import com.google.code.bigyang.base.impl.AbstractSession;
import com.google.code.bigyang.buffer.Buffer;
import com.google.code.bigyang.buffer.NioBuffer;
import com.google.code.bigyang.filter.impl.DefaultFilterChain;

public abstract class NioSession extends AbstractSession {

	protected SelectableChannel channel;
	
	protected NioBuffer buffer;
	
	protected SelectorSet manager;
	
	public NioSession() {
		this.setChain(new DefaultFilterChain(this));
	}
	
	public void interestKey(SelectionKey key, int op) {
		if(key.attachment() != null) {
			key.attach(this);
		}
		key.interestOps( key.interestOps() | op );
	}
	
	
	public final void enableReader(Selector selector) {
		SelectionKey key = null;
		if(selector != null) {
			key = channel.keyFor(selector);
		}
		if(key != null && key.isValid()) {
			interestKey(key, SelectionKey.OP_READ);
		} else {
			try {
				channel.register(selector, SelectionKey.OP_READ,this);
			} catch (ClosedChannelException e) {
				e.printStackTrace();
			}
		}
	}
	
	
	public final void doFilter(NioBuffer buffer) {
		for(Filter f:this.filters) {
			f.doFilter(buffer);
		}
	}
	
	
	@SuppressWarnings("unused")
	public void readable() throws IOException {
		this.getBuffer().buf();
		ReadableByteChannel readChannel = (ReadableByteChannel)channel;
		int count = -1;
		int total = 0;
		ByteBuffer buf = this.buffer.buf();
		while( (count=readChannel.read(buf) ) > 0) {
			total += count;
		}
		this.buffer.filp();
//		doFilter(buffer);
		
		
		this.getChain().fireMessageReceived(buffer);
		
//		handleEvent(SessionState.ENABLE_READ, null);
		NioSessionEvent event = new NioSessionEvent(this, SessionState.ENABLE_READ);
		manager.registerEvent(event);
	}
	
	public void decode() {
		
	}
	
	
	@SuppressWarnings("unused")
	public final void handleEvent(SessionState state,Selector selector) throws IOException {
		if(selector != null) {
			SelectionKey key = channel.keyFor(selector);
		}
		
		switch(state) {
			case ENABLE_READ:
				enableReader(selector);
				break;
			
			case ENABLE_WRITE:
				readable();
				break;
			
			case READ_ABLE:
				readable();
				break;
				
			case WRITE_ABLE:
				//...
				break;
				
			case EXPIRE:
				//...
				break;
				
			case CLOSE:
				//...
				break;
				
			case CONNECTED:
				handleConnected();
				break;
		}
	}

	protected void handleConnected() {
		handler.onCreate(this);
	}
	

	public abstract int read(Buffer buf);


	@Override
	public void write(Object message) {
		// TODO Auto-generated method stub
		
	}


	public SelectableChannel getChannel() {
		return channel;
	}


	public NioBuffer getBuffer() {
		return buffer;
	}


	public void setChannel(SelectableChannel channel) {
		this.channel = channel;
	}


	public void setBuffer(NioBuffer buffer) {
		this.buffer = buffer;
	}


	public SelectorSet getManager() {
		return manager;
	}


	public void setManager(SelectorSet manager) {
		this.manager = manager;
	}
	
	
}
